<template>
  <!-- 查询表单 card -->
  <SearchForm
    v-show="isShowSearch"
    :search="handleSearch"
    :reset="handleReset"
    :columns="searchColumns"
    :search-param="searchParam"
    :search-col="searchCol"
    :show-reset="props.tableOptions.showReset"
  />

  <!-- 表格内容 card -->
  <div class="card table-main">
    <!-- 表格头部 操作按钮 -->
    <div class="table-header">
      <div class="header-button-lf">
        <slot name="tableHeader" :selected-list-ids="selectedListIds" :selected-list="selectedList" :is-selected="isSelected">
          <el-button type="primary" :icon="CirclePlus" @click="handleBtnAction(operateType.ADD, { id: '' })"> 新增 </el-button>
          <el-button
            type="danger"
            :icon="Delete"
            :disabled="!isSelected"
            @click="handleBtnAction(operateType.BATCH_DEL, selectedListIds)"
          >
            删除
          </el-button>
        </slot>
      </div>
      <div v-if="toolButton" class="header-button-ri">
        <slot name="toolButton">
          <el-button :icon="Refresh" circle @click="getTableList" />
          <el-button v-if="tableColumns.length" :icon="Printer" circle @click="print" />
          <el-button v-if="tableColumns.length" :icon="Operation" circle @click="openColSetting" />
          <el-button v-if="searchColumns.length" :icon="Search" circle @click="isShowSearch = !isShowSearch" />
        </slot>
      </div>
    </div>
    <!-- 表格主体 -->
    <vxe-table
      :max-height="tableMaxHeight"
      :key="tableKey"
      ref="tableRef"
      v-bind="$attrs"
      :data="data ?? tableData"
      :border="border"
      :stripe="true"
      :row-config="{
        useKey: true,
        isHover: true,
        keyField: isTree ? 'id' : '',
        height: 32
      }"
      :tree-config="
        isTree
          ? {
              transform: true,
              rowField: treeRowField,
              parentField: treeParentField,
              expandRowKeys: expandRowKeys
            }
          : {}
      "
      :column-config="{
        resizable: true
      }"
      :sort-config="{ remote: true }"
      @checkbox-all="selectionChange"
      @checkbox-change="selectionChange"
      @cell-click="handleCellClick"
      @cell-dblclick="handleCellDBClick"
      @sort-change="handleSorted"
    >
      <!-- 默认插槽 -->
      <slot></slot>
      <template v-for="item in tableColumns" :key="item">
        <!-- selection || index || expand -->
        <!-- <vxe-column v-if="item.type && ['selection', 'index', 'expand'].includes(item.type)">
          <template v-if="item.type == 'expand'" #default="scope">
            <component :is="item.render" v-bind="scope" v-if="item.render"> </component>
            <slot v-else :name="item.type" v-bind="scope"></slot>
          </template>
        </vxe-column> -->
        <vxe-column
          v-if="item.type === TableColumnOrFormTypeEnum.SELECTION"
          :min-width="item.minWidth"
          type="checkbox"
          :width="item.width || '60px'"
          :align="'center'"
          fixed="left"
        >
        </vxe-column>
        <vxe-column
          v-if="item.type === TableColumnOrFormTypeEnum.TREENODE"
          :min-width="item.minWidth"
          type="seq"
          :align="'center'"
          :width="item.width || '60px'"
          :title="item.label"
          tree-node
        ></vxe-column>
        <vxe-column
          v-if="item.type === TableColumnOrFormTypeEnum.INDEX"
          :min-width="item.minWidth"
          title="序号"
          type="seq"
          fixed="left"
          :align="'center'"
          :width="item.width || '60px'"
        >
        </vxe-column>
        <!-- other -->
        <TableColumn v-if="item.prop && item.isShow" :column="item" :align="handleAlign(item)">
          <template v-for="slot in Object.keys($slots)" #[slot]="scope">
            <slot :name="slot" v-bind="scope"></slot>
          </template>
        </TableColumn>
      </template>
      <!-- 插入表格最后一行之后的插槽 -->
      <template #append>
        <slot name="append"> </slot>
      </template>
      <!-- 无数据 -->
      <template #empty>
        <div class="table-empty">
          <slot name="empty">
            <img src="@/assets/images/notData.png" alt="notData" />
            <div>暂无数据</div>
          </slot>
        </div>
      </template>
    </vxe-table>
    <!-- 分页组件 -->
    <slot name="pagination">
      <Pagination
        v-if="props.tableOptions.pageable"
        :page-sizes="[10, 20, 50, 100, 500, 1000]"
        :pageInfo="pageInfo"
        :handle-size-change="handleSizeChange"
        :handle-current-change="handleCurrentChange"
      />
    </slot>
  </div>
  <!-- 列设置 -->
  <ColSetting v-if="toolButton" ref="colRef" v-model:col-setting="colSetting" />
</template>

<script setup lang="ts" name="KingTable">
import { ref, watch, computed, provide, onMounted, nextTick } from "vue";
import { useTable } from "@/hooks/useKingTable";
import { useSelection } from "@/hooks/useTableSelection";
import { BreakPoint } from "@/components/Grid/interface";
import { TableColumnProps, SearchColumnProps } from "@/components/KingTable/interface";
import { Refresh, Printer, Operation, Search } from "@element-plus/icons-vue";
import { filterEnum, formatValue, handleProp, handleRowAccordingToProp } from "@/utils";
import SearchForm from "@/components/KingSearch/index.vue";
import Pagination from "./components/Pagination.vue";
import ColSetting from "./components/ColSetting.vue";
import TableColumn from "./components/TableColumn.vue";
import printJS from "print-js";
import { TableColumnOrFormTypeEnum } from "@/plugins/commonType.enum";
import { backTableHeight, getLocalStorage } from "@/api/mains";
import { postRequest, getByPath } from "@/api/mains";
import { useHandleData } from "@/hooks/useHandleData";
import { succMsg, confirm } from "@/utils/MessageUtils";
import { ElLoading } from "element-plus";
import { CirclePlus, Delete } from "@element-plus/icons-vue";
import { operateType } from "@/plugins/commonType.enum";
import Sortable from "sortablejs";

const emit = defineEmits(["handle-cell-db-click", "handle-cell-click", "handle-btn-click"]);

export interface KingTableProps {
  // columns: TableColumnProps[]; // 列配置项  ==> 必传
  data?: any[]; // 静态 table data 数据，若存在则不会使用 requestApi 返回的 data ==> 非必传
  requestApi?: (params: any) => Promise<any>; // 请求表格数据的 api ==> 非必传
  requestAuto?: boolean; // 是否自动执行请求 api ==> 非必传（默认为true）
  requestError?: (params: any) => void; // 表格 api 请求错误监听 ==> 非必传
  doBeforeQuery?: (params: any) => any; // 请求前参数处理 ==> 非必传
  dataCallback?: (data: any) => any; // 返回数据的回调函数，可以对数据进行处理 ==> 非必传
  // title?: string; // 表格标题，目前只在打印的时候用到 ==> 非必传
  // pagination?: boolean; // 是否需要分页组件 ==> 非必传（默认为true） xxxxxxxxx
  // initParam?: any; // 初始化请求参数 ==> 非必传（默认为{}）
  border?: boolean; // 是否带有纵向边框 ==> 非必传（默认为true）
  toolButton?: boolean; // 是否显示表格功能按钮 ==> 非必传（默认为true）
  rowKey?: string; // 行数据的 Key，用来优化 Table 的渲染，当表格数据多选时，所指定的 id ==> 非必传（默认为 id）
  searchCol?: number | Record<BreakPoint, number>; // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }
  // maxHeight?: number;
  tableOptions: any;
  // isTree: false;
  // treeRowField: "id";
  // treeParentField: "parentid";
}

// 接受父组件参数，配置默认值
const props = withDefaults(defineProps<KingTableProps>(), {
  // columns: () => [],
  requestAuto: true,
  // pagination: true, //xxxxxxxxxxxxxx
  // initParam: {},
  border: true,
  toolButton: true,
  rowKey: "id",
  searchCol: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }),
  tableOptions: {}
});

// 是否显示搜索模块
const isShowSearch = ref(true);

// 表格 DOM 元素
const tableRef = ref();
// 表格key
const tableKey = ref(Math.random());
// Sortable 列拖拽
const sortableColRef = ref();
// Sortable 行拖拽
const sortableRowRef = ref();

Sortable;

const isTree = ref(props.tableOptions.isTree);
const expandRowKeys = ref([]);
const isExpandAllTree = ref(false); //展开树table
const treeRowField = ref(props.tableOptions.treeRowField ? props.tableOptions.treeRowField : "id");
const treeParentField = ref(props.tableOptions.treeParentField ? props.tableOptions.treeParentField : "parentid");

const isDragRow = ref(props.tableOptions.isDragRow ? props.tableOptions.isDragRow : false);
const isDragCol = ref(props.tableOptions.isDragCol ? props.tableOptions.isDragCol : false);

const actualMaxHeight = props.tableOptions.tableMaxHeight
  ? props.tableOptions.tableMaxHeight
  : isTree.value == true
  ? backTableHeight()
  : backTableHeight() - 50;
const tableMaxHeight = ref(actualMaxHeight);

//设置默认值>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
if (typeof props.tableOptions.queryAuto == "undefined") {
  props.tableOptions.queryAuto = true;
}
if (typeof props.tableOptions.pageable == "undefined") {
  props.tableOptions.pageable = true;
}
if (typeof props.tableOptions.showReset == "undefined") {
  props.tableOptions.showReset = true;
}

// 表格多选 Hooks
const { selectionChange, selectedList, selectedListIds, isSelected } = useSelection(props.rowKey);

/**
 * 双击单元格
 */
const handleCellDBClick = (params: any) => {
  emit("handle-cell-db-click", params);
};

/**
 * 单击单元格
 */
const handleCellClick = (params: any) => {
  emit("handle-cell-click", params);
};

/**
 * 增删查按钮执行的业务操作
 * @param {*} type 当前执行操作的类型
 * @param {*} row
 */
const handleBtnAction = async (type: string, row: any) => {
  emit("handle-btn-click", type, row);
};

let defaultOrder: string = ""; //默认的排序
/**
 * 点击table排序
 * @param {} val
 */
const handleSorted = async (sortValue: any) => {
  const { field, order } = sortValue;
  //重复点同一个箭头排序，会出现null，所以在null时应记住上一次的排序
  if (order != null) {
    defaultOrder = order;
  }
  const val = {
    prop: field,
    order: defaultOrder === "desc" ? "desc" : defaultOrder
  };

  doSorted(val, searchInitParam.value);
  getTableList();
};

//排序
const doSorted = (val: any, data: any) => {
  if (val.prop) {
    if (val.order == "desc") {
      data.sorts = `${val.prop}@desc`;
    } else {
      data.sorts = `${val.prop}@asc`;
    }
  }
};

// 表格操作 Hooks
const {
  tableData,
  pageInfo,
  searchParam,
  searchInitParam,
  getTableList,
  handleSearch,
  handleReset,
  handleSizeChange,
  handleCurrentChange
} = useTable(
  props.requestApi,
  props.tableOptions.queryUrl,
  props.tableOptions.initParam,
  props.tableOptions.pageable,
  props.tableOptions.doBeforeQuery,
  props.dataCallback,
  props.requestError
);

// 清空选中数据列表
const clearSelection = () => tableRef.value!.clearSelected(); //clearSelection

// 删除用户
const deleteByIds = (params: { id: string[] }) => {
  return postRequest(
    props.tableOptions.delManyUrl ? props.tableOptions.delManyUrl : props.tableOptions.operUrl + `/delete`,
    params
  );
};

const handleDelete = (row: any) => {
  if (row.id) {
    confirm("你确定要删除当前项吗", async () => {
      const loading = ElLoading.service({
        lock: true,
        text: "删除中，请稍候...",
        background: "rgba(0, 0, 0, 0.7)"
      });
      const res: any = await getByPath(
        props.tableOptions.delOneUrl ? props.tableOptions.delOneUrl : props.tableOptions.operUrl + "/delete",
        row.id
      );
      loading.close();
      if (res.code == 200) {
        succMsg(res.msg);
        getTableList();
      }
    });
  }
};

// 批量删除
const handleBatchDelete = async () => {
  await useHandleData(deleteByIds, selectedListIds.value, "删除所选记录");
  clearSelection();
  getTableList();
};

// 接收 tableOptions.tableColumns 并设置为响应式
const tableColumns = ref<TableColumnProps[]>(props.tableOptions.tableColumns);

// 定义 enumMap 存储 enum 值（避免异步请求无法格式化单元格内容 || 无法填充搜索下拉选择）
const enumMap = ref(new Map<string, { [key: string]: any }[]>());
provide("enumMap", enumMap);

const setEnumMap = async (col: TableColumnProps) => {
  if (col.dictCode && !enumMap.value.get(col.dictCode)) {
    const enumData = getLocalStorage()[col.dictCode];
    enumMap.value.set(col.dictCode!, enumData);
  }
};

// 扁平化 tableColumns
const flatColumnsFunc = (columns: TableColumnProps[], flatArr: TableColumnProps[] = []) => {
  columns.forEach(async col => {
    if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children));
    flatArr.push(col);

    // 给每一项 column 添加 isShow && isFilterEnum 默认属性
    col.isShow = col.isShow ?? true;
    col.isFilterEnum = col.isFilterEnum ?? true;

    // 设置 enumMap
    setEnumMap(col);
  });
  return flatArr.filter(item => !item._children?.length);
};

// flatColumns
const flatColumns = ref<TableColumnProps[]>();
flatColumns.value = flatColumnsFunc(tableColumns.value);

// 过滤需要搜索的配置项
const searchColumns = ref<SearchColumnProps[]>(props.tableOptions.searchColumns || []); //flatColumns.value.filter(item => item.search?.el || item.search?.render);

// 设置搜索表单排序默认值 && 设置搜索表单项的默认值
searchColumns.value.forEach((column /* , index */) => {
  // column.order = column.order ?? index + 2;
  if (typeof column.showLabel == "undefined") {
    column.showLabel = true;
  }
  if (column.defaultValue !== undefined && column.defaultValue !== null) {
    searchInitParam.value[column.key ?? handleProp(column.prop!)] = column.defaultValue;
    searchParam.value[column.key ?? handleProp(column.prop!)] = column.defaultValue;
  }
});

// 排序搜索表单项
// searchColumns.value.sort((a, b) => a.order! - b.order!);

// 列设置 ==> 过滤掉不需要设置的列
const colRef = ref();
const colSetting = tableColumns.value!.filter(
  item => !["selection", "index", "expand"].includes(item.type!) && item.prop !== "operation" && item.isShow
);
const openColSetting = () => colRef.value.openColSetting();

// 🙅‍♀️ 不需要打印可以把以下方法删除，打印功能目前存在很多 bug
// 处理打印数据（把后台返回的值根据 enum 做转换）
const printData = computed(() => {
  const handleData = props.data ?? tableData.value;
  const printDataList = JSON.parse(JSON.stringify(selectedList.value.length ? selectedList.value : handleData));
  // 找出需要转换数据的列（有 enum || 多级 prop && 需要根据 enum 格式化）
  const needTransformCol = flatColumns.value!.filter(
    item => (item.enum || (item.prop && item.prop.split(".").length > 1)) && item.isFilterEnum
  );
  needTransformCol.forEach(colItem => {
    printDataList.forEach((tableItem: { [key: string]: any }) => {
      tableItem[handleProp(colItem.prop!)] =
        colItem.prop!.split(".").length > 1 && !colItem.enum
          ? formatValue(handleRowAccordingToProp(tableItem, colItem.prop!))
          : filterEnum(handleRowAccordingToProp(tableItem, colItem.prop!), enumMap.value.get(colItem.prop!), colItem.fieldNames);
      for (const key in tableItem) {
        if (tableItem[key] === null) tableItem[key] = formatValue(tableItem[key]);
      }
    });
  });
  return printDataList;
});

// 打印表格数据（💥 多级表头数据打印时，只能扁平化成一维数组，printJs 不支持多级表头打印）
const print = () => {
  const header = `<div style="text-align: center"><h2>${props.tableOptions.title}</h2></div>`;
  const gridHeaderStyle = "border: 1px solid #ebeef5;height: 45px;color: #232425;text-align: center;background-color: #fafafa;";
  const gridStyle = "border: 1px solid #ebeef5;height: 40px;color: #494b4e;text-align: center";
  printJS({
    printable: printData.value,
    header: props.tableOptions.title && header,
    properties: flatColumns
      .value!.filter(item => !["selection", "index", "expand"].includes(item.type!) && item.isShow && item.prop !== "operation")
      .map((item: TableColumnProps) => ({ field: handleProp(item.prop!), displayName: item.label })),
    type: "json",
    gridHeaderStyle,
    gridStyle
  });
};

const handleAlign = (item: any) => {
  return item.align || "center";
};

/**
 * 展开/折叠树
 */
const handleExpandTree = () => {
  isExpandAllTree.value = !isExpandAllTree.value;
  tableRef.value.setAllTreeExpand(isExpandAllTree.value);
};

// 列拖拽
const colDrag = (event?: { preventDefault: () => void }) => {
  event?.preventDefault();
  nextTick(() => {
    const wrapper: HTMLElement = document.querySelector(".vxe-table--header-wrapper tr")!;
    sortableColRef.value = Sortable.create(wrapper, {
      animation: 100,
      delay: 0,
      onEnd: ({ newIndex, oldIndex }) => {
        // 换序操作
        let currRow = tableColumns.value.splice(oldIndex!, 1)[0];
        tableColumns.value.splice(newIndex!, 0, currRow);
        tableRef.value.reloadColumn(tableColumns.value);
        tableKey.value = Math.random();
      }
    });
  });
};

// 行拖拽
const rowDrag = (event?: { preventDefault: () => void }) => {
  event?.preventDefault();
  nextTick(() => {
    const wrapper: HTMLElement = document.querySelector(".vxe-table--body tbody")!;
    sortableRowRef.value = Sortable.create(wrapper, {
      animation: 100,
      delay: 0,
      onEnd: ({ newIndex, oldIndex }) => {
        const handleData = props.data ?? tableData.value;
        const currRow = handleData.splice(oldIndex!, 1)[0];
        handleData.splice(newIndex!, 0, currRow);
        tableKey.value = Math.random();
      }
    });
  });
};

// 初始化请求
onMounted(() => {
  props.tableOptions.queryAuto && getTableList();

  nextTick(() => {
    if (isDragCol.value) {
      colDrag(event);
    }
    if (isDragRow.value) {
      rowDrag(event);
    }
  });

  // if (isTree.value == true) {
  //   //默认展开树
  //   setTimeout(function () {
  //     const rootRow = tableRef.value.getData(0);
  //     if (rootRow) {
  //       tableRef.value.setTreeExpand([rootRow], true);
  //     }
  //   }, 1000); // 定时时间
  // }
});

// 监听页面 initParam 改化，重新获取表格数据
watch(() => props.tableOptions.initParam, getTableList, { deep: true });

// 监听页面 tableData 改化，重新获取表格数据
watch(
  () => tableData,
  newVal => {
    if (newVal == null) {
      return;
    }
    const rootRow = tableRef.value.getData(0);
    if (isTree.value && rootRow) {
      tableRef.value.setTreeExpand([rootRow], true);
    }
  },
  { deep: true }
);

// 监听页面 initParam 改化，重新获取表格数据
watch(
  () => tableKey,
  newVal => {
    if (newVal == null) {
      return;
    }
    // 列拖拽
    if (sortableColRef.value) {
      sortableColRef.value.destroy();
    }
    colDrag(event);
    // 行拖拽
    if (sortableRowRef.value) {
      sortableRowRef.value.destroy();
    }
    rowDrag(event);
  },
  { deep: true }
);

// 暴露给父组件的参数和方法(外部需要什么，都可以从这里暴露出去)
defineExpose({
  element: tableRef,
  tableData,
  pageInfo,
  searchParam,
  searchInitParam,
  tableMaxHeight,
  getTableList,
  handleSearch,
  handleReset,
  handleSizeChange,
  handleCurrentChange,
  clearSelection,
  handleDelete,
  handleBatchDelete,
  handleAlign,
  handleExpandTree,
  enumMap,
  isSelected,
  selectedList,
  selectedListIds
});
</script>
<style lang="scss" scoped>
:deep(.vxe-table--body) {
  .vxe-body--row {
    height: 40px;
  }
  .vxe-body--column {
    padding: 8px 0;
  }
}
</style>
